package com.esp.studentManagementSystem.service.impl;

import com.esp.studentManagementSystem.entity.*;
import com.esp.studentManagementSystem.pojo.po.entity.*;
import com.esp.studentManagementSystem.pojo.po.entity.ID.RoleMenuId;
import com.esp.studentManagementSystem.pojo.vo.MenuVO;
import com.esp.studentManagementSystem.pojo.vo.RoleVO;
import com.esp.studentManagementSystem.pojo.vo.UserMenuVO;
import com.esp.studentManagementSystem.service.UserMenuService;
import com.esp.studentManagementSystem.utils.MenuConverterUtils;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class UserMenuServiceImpl implements UserMenuService {
    @Autowired
    private JPAQueryFactory jpaQuery;

    private static final Logger log = LoggerFactory.getLogger(UserMenuServiceImpl.class);

    /**
     * 已知user_id
     * ①在sys_user表中查询username
     * ②在sys_role_user表中查询rid
     * ③在sys_role表中查询role
     * ④在sys_role_menu表中查询mid
     * ⑤在sys_menu表中查询menu
     * @param userId userId
     * @return UserMenuDTO
     */
    public UserMenuVO getUserMenu(Integer userId) {
        //在sys_user表中查询username
        List<SysUser> sysUsers = this.getSysUsers(userId);

        //在sys_role_user表中查询rid（可能有多个）
        List<SysRoleUser> sysRoleUsers = this.getSysRoleUsers(sysUsers);

        //在sys_role表中查询role（关联rid，可能有多个）
        List<SysRole> sysRoles = this.getSysRoles(sysRoleUsers);

        //在sys_role_menu表中查询mid（使用sysRoles列表中的数据，可能有多个）
        Set<Integer> menuIds = this.getMenuIds(sysRoles);

        //在sys_menu表中查询menu
        List<SysMenu> sysMenus = this.getSysMenus(menuIds);

        //封装返回UserMenuDTO
        return this.getUserMenuDTO(sysUsers, sysRoles, sysMenus);
    }

    /**
     * 根据userId获取用户信息
     * @param userId userId 用户的id
     * @return List<SysUser> 返回用户信息列表，由于userId唯一，所以列表中只有一个元素
     */
    private List<SysUser> getSysUsers(Integer userId) {
        QSysUser qSysUser = QSysUser.sysUser;
        return jpaQuery.select(qSysUser).from(qSysUser).where(qSysUser.userId.eq(userId)).fetch();
    }

    /**
     * 根据sysUsers获取sysRoleUsers
     * @param sysUsers sysUsers 用户信息列表
     * @return List<SysRoleUser> 角色-用户对应关系列表，可能存在多个
     */
    private List<SysRoleUser> getSysRoleUsers(List<SysUser> sysUsers) {
        QSysRoleUser qSysRoleUser = QSysRoleUser.sysRoleUser;
        return jpaQuery.select(qSysRoleUser).from(qSysRoleUser).where(qSysRoleUser.id.uid.eq(sysUsers.get(0).getUserId())).fetch();
    }

    /**
     * 根据sysRoleUsers获取sysRoles
     * @param sysRoleUsers sysRoleUsers 角色-用户对应关系列表
     * @return List<SysRole> 角色列表，可能存在多个
     */
    private List<SysRole> getSysRoles(List<SysRoleUser> sysRoleUsers) {
        QSysRole qSysRole = QSysRole.sysRole;
        // 创建集合存储角色id并去重
        Set<Integer> roleIds = new HashSet<>();
        for (SysRoleUser sysRoleUser : sysRoleUsers) {
            roleIds.add(sysRoleUser.getId().getRid());
        }
        //一次性查询
        return jpaQuery.select(qSysRole).from(qSysRole).where(qSysRole.id.in(roleIds)).fetch();
    }

    /**
     * 根据sysRoles获取sysRoleMenus
     * @param sysRoles sysRoles 角色列表
     * @return Set<Integer> 该用户角色对应的权限id集合，因角色可能有多个，权限可能有重复，所以使用集合存储
     */
    private Set<Integer> getMenuIds(List<SysRole> sysRoles) {
        try {
            QSysRoleMenu qSysRoleMenu = QSysRoleMenu.sysRoleMenu;
            // 获取角色ID列表
            List<Integer> roleIds = sysRoles.stream().map(SysRole::getId).collect(Collectors.toList());
            // 一次性查询
            List<SysRoleMenu> sysRoleMenus = jpaQuery.select(qSysRoleMenu).from(qSysRoleMenu).where(qSysRoleMenu.id.rid.in(roleIds)).fetch();
            // 获取所有的菜单ID
            return sysRoleMenus.stream().map(SysRoleMenu::getId).map(RoleMenuId::getMid).collect(Collectors.toSet());
        } catch (Exception e) {
            log.error("UserMenuService getMenuIds error: {}", e.getMessage());
            return Collections.emptySet();
        }
    }

    /**
     * 根据menuIds获取sysMenus
     * @param menuIds menuIds 用户角色对应的权限id集合
     * @return List<SysMenu> 权限菜单列表
     */
    private List<SysMenu> getSysMenus(Set<Integer> menuIds) {
        if (menuIds == null || menuIds.isEmpty()) {
            return new ArrayList<>(); // 当menuIds为空时，直接返回空列表
        }
        QSysMenu qSysMenu = QSysMenu.sysMenu;
        // 创建用于存储权限的列表
        List<SysMenu> sysMenus = new ArrayList<>();
        // 创建用于存储需要查询的父菜单id
        List<Integer> pidToFetch = new ArrayList<>();
        for (Integer menuId : menuIds) {
            List<SysMenu> foundMenus = jpaQuery.select(qSysMenu).from(qSysMenu).where(qSysMenu.id.eq(menuId)).fetch();
            // 若处理权限id无对应的权限，则不给予任何操作
            if (!foundMenus.isEmpty()) {
                SysMenu sysMenu = foundMenus.get(0);
                sysMenus.add(sysMenu);
                Integer pid = sysMenu.getPid();
                if (pid != null && !menuIds.contains(pid) && !pidToFetch.contains(pid)) {
                    // 添加父菜单ID到待查询列表
                    pidToFetch.add(pid);
                }
            }
        }
        // 处理权限中只有子菜单没有父菜单的情况，为其添加父菜单
        List<SysMenu> parentMenus = pidToFetch.stream()
                .map(pid -> jpaQuery.select(qSysMenu).from(qSysMenu).where(qSysMenu.id.eq(pid)).fetch())
                .flatMap(Collection::stream)
                .toList();
        sysMenus.addAll(parentMenus);
        return sysMenus;
    }

    /**
     * 封装返回UserMenuDTO
     * @param sysUsers 用户列表
     * @param sysRoles 角色列表
     * @param sysMenus 菜单列表
     * @return UserMenuDTO 封装为UserMenuDTO，用于数据传输
     */
    private UserMenuVO getUserMenuDTO(List<SysUser> sysUsers, List<SysRole> sysRoles, List<SysMenu> sysMenus) {
        // 检查sysUsers是否为空或空列表
        if (sysUsers == null || sysUsers.isEmpty()) {
            throw new IllegalArgumentException("sysUsers cannot be null or empty, and the first element must not be null.");
        }
        // 创建一个UserMenuDTO对象用于最终封装
        UserMenuVO userMenuVO = new UserMenuVO();
        // 放置userId
        userMenuVO.setUserId(sysUsers.get(0).getUserId());
        // 放置username
        userMenuVO.setUsername(sysUsers.get(0).getUsername());
        // 检查sysRoles是否为null或空列表
        if (sysRoles != null && !sysRoles.isEmpty()) {
            List<RoleVO> roleVOS = new ArrayList<>();
            // sysRole转为RoleBO形式
            for (SysRole sysRole : sysRoles) {
                RoleVO roleVO = new RoleVO();
                // 放置role的id
                roleVO.setId(sysRole.getId());
                // 放置role的rolename
                roleVO.setRolename(sysRole.getRoleName());
                // 放置role的remark
                roleVO.setRemark(sysRole.getRemark());
                roleVOS.add(roleVO);
            }
            // 将roleBOs放入userMenuDTO
            userMenuVO.setRoles(roleVOS);
        }
        // 检查sysMenus是否为null或空列表
        if (sysMenus != null && !sysMenus.isEmpty()) {
            try {
                // 将sysMenus转换为MenuBO形式
                List<MenuVO> menuVOS = MenuConverterUtils.convertToList(sysMenus);
                // 将MenuBOs放入userMenuDTO
                userMenuVO.setMenus(menuVOS);
            } catch (Exception e) {
                throw new RuntimeException("Error converting sysMenus to MenuBOs.", e);
            }
        }
        return userMenuVO;
    }
}
